TypeScript is an easy to learn extension of JavaScript. It’s easy to write programs that run and does something. However, it’s hard to account for all the uses cases and write robust TypeScript code.
In this article, we’ll look at the best practices to following when writing code with TypeScript, including using includes
over indexOf
.
Also, we look at why we should use Regex.exec
instead of String.match
and why we should use the namespace
keyword instead of using the module
keyword to declare TypeScript modules.
Using includes Over indexOf
We should use includes
over indexOf
. It’s shorter and does the same thing.
There’s an includes
method for both strings and arrays.
For instance, instead of writing:
const arr = [1, 2, 3];
const hasOne = arr.indexOf(1) !== -1;
or:
const str = 'foobar';
const hasFoo = str.indexOf(1) !== -1;
We write:
const arr = [1, 2, 3];
const hasOne = arr.includes(1);
or:
const str = 'foobar'
const hasFoo = str.includes(1);
includes
finds check if an instance of something exists in a string or an array and returns true
if it exists and false
otherwise.
Require the use of the namespace Keyword Instead of module to Declare TypeScript Modules
To prevent any confusion with ES6 modules, we should use the namespace
keyword to declare TypeScript modules.
For instance, instead of writing:
`module` Shapes {
export class Circle { /* ... */ }
export class Square { /* ... */ }
}
we write:
export namespace Shapes {
export class Circle { /* ... */ }
export class Square { /* ... */ }
}
Use Nullish Coalescing Operator Instead of Logical Chaining
Now that TypeScript has the nullish coaslescing operator, we should use that instead of logical chaining.
For instance, instead of writing:
function func(foo: string | null) {
return foo || 'hello';
}
We should write:
function func(foo: string | null) {
return foo ?? 'hello';
}
??
is better since it returns 'hello'
only if foo
is null
or undefined
.
Use Concise Optional Chain Expressions Instead of Chained Logical And
Optional chaining expressions are better than a long chain of logical &&
‘s.
Therefore, we should use that instead of AND expressions.
For instance, instead of writing:
function bar(foo: T | null) {
return foo && foo.a && foo.a.b && foo.a.b.c;
}
We write:
function bar(foo: T | null) {
return foo?.a?.b?.c;
}
As we can see, it’s much shorter and they do the same thing.
Private Members Should be Marked as readonly if they’re Never Modified Outside of a Constructor
We should mark private members of a class as readonly
if they’re never been changed outside of a constructor.
This way, we can prevent them from being changed accidentally anywhere else.
For instance, instead of writing:
class Foo {
private foo: string;
public constructor() {
this.foo = "bar";
}
}
We write:
class Foo {
private readonly foo: string;
public constructor() {
this.foo = "bar";
}
}
Now we can’t change this.foo
anywhere in the class.
Use Type Parameter When Calling array.reduce Instead of Casting
If we use array.reduce
with generic types, like an array or object, then we should specify a type parameter so we can specify a useful type for the initial value.
We can do that with a type parameter instead of a type assertion.
For instance, instead of writing:
[1, 2, 3].reduce((acc, num) => acc + num, 0 as number);
We write:
[1, 2, 3].reduce<number>((acc, num) => acc + num, 0);
Now we enforce the type on everything instead of just the second argument.
Use Regex.exec Instead of String.match if no Global Flag is Provided
Regex.exec
is after than String.match
and they both work the same if the /g
flag isn’t added.
For instance, instead of writing:
'foobar'.match(/bar/);
We write:
/bar/.exec('foobar');
However, if we want to get all results of a pattern, then we’ve to use String.match
:
'555-1212'.match(/d+/g);
Conclusion
We should use includes
instead of indexOf
to check if something is in a string or array.
Also, we should use Regex.exec
as much as possible instead of String.match
for finding single matches.
To declare a TypeScript module, use the namespace
keyword instead of module
.